package fr.n7.sma.core;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.event.EventListenerList;

import fr.n7.sma.model.Agent;
import fr.n7.sma.model.Cell;
import fr.n7.sma.model.GridModel;
import fr.n7.sma.util.property.PropertyChangedEvent;
import fr.n7.sma.util.property.PropertyListener;
import fr.n7.sma.util.property.PropertyManager;
import fr.n7.sma.view.Grid;
import fr.n7.sma.view.ResourceCharts;
import fr.n7.sma.view.Grid.Properties;
import fr.n7.sma.view.listeners.IterationListener;
import fr.n7.sma.view.model.AgentViewProperties;
import fr.n7.sma.view.model.CellViewProperties;

public class Main extends JPanel implements PropertyListener<Properties> {

	private static final long serialVersionUID = -5280260847095696259L;

	private Grid grid;
	private GridModel<Cell<CellViewProperties>, AgentViewProperties, CellViewProperties> gridModel;
//	private Map<String, BufferedImage> resourceSymbols;
	private int drawingRefreshRate;
	private int chartsRefreshRate;
	private boolean isPaused;
//	private boolean isStopped;
	private int pause;

	private boolean realTime;

	private PropertyManager<Properties> gridProps;

	private int iter;

	private EventListenerList listeners;

	private ResourceCharts resourceCharts;

	private Boolean evapOn;

	public void setPause(int pause){
		this.pause = pause;
	}

	public void play(){
//		isStopped = false;
		isPaused = false;
//		if(isPaused){
//		isPaused = !isPaused;
//		synchronized (this) {
//		this.notify();
//		}
//		}else{

		while(!isPaused){
			nextStep();
			if((iter+1)%drawingRefreshRate == 0){
				if(!realTime){
					grid.waitCompleteRedraw();
				}
			}
			if((iter+1)%chartsRefreshRate == 0){
//				majCharts();
			}
			try {
				synchronized (this) {
					if(realTime) this.wait(pause);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
//		}
	}

	public void doSteps(int nbIter){
		isPaused = false;
		for(int i=0; i<nbIter; i++){
			nextStep();
			if((iter+1)%drawingRefreshRate == 0){
				if(!realTime){
					grid.waitCompleteRedraw();
				}
			}
			if((iter+1)%chartsRefreshRate == 0){
				majCharts();
			}
			try {
				synchronized (this) {
					if(realTime) this.wait(pause);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(isPaused){
				break;
			}
		}
		grid.completeRedraw();
	}

	private void nextStep(){
		synchronized (gridModel.getAgents()) {
			for(Agent<AgentViewProperties, CellViewProperties> a : gridModel.getAgents()){
				a.doNextStep();
			}
		}
		iter++;
		if(evapOn ){
			gridModel.evaporate();
		}
		fireIteration();
	}

	public void doOneStep(){
		nextStep();
		if(!realTime){
			grid.waitCompleteRedraw();
		}
		majCharts();
	}

	public void majCharts(){
		resourceCharts.majGUI();
	}

	public void addIterationListener(IterationListener l){
		listeners.add(IterationListener.class, l);
	}

	public void removeIterationListener(IterationListener l){
		listeners.remove(IterationListener.class, l);
	}

	private void fireIteration(){
		for(IterationListener l : listeners.getListeners(IterationListener.class)){
			l.updateIteration(iter);
		}
	}

	public GridModel<Cell<CellViewProperties>, AgentViewProperties, CellViewProperties> getGridModel() {
		return gridModel;
	}

	public Grid getGrid() {
		return grid;
	}

	public void pause(){
		isPaused = true;
	}

	public void stop(){
//		isStopped = true;
	}

	public void setResourceColors(Map<String, Color> resourceColors){
		grid.setResourcesColors(resourceColors);
	}

	public Main(
			int w,
			int h,
			Map<String, BufferedImage> resourceSymbols,
			Map<String, Color> resourceColors,
			PropertyManager<Properties> propertyManager,
			Dimension dimension,
			ResourceCharts resourceCharts){

		create(w, h, resourceSymbols,
				resourceColors, propertyManager, dimension, resourceCharts);
	}

	@SuppressWarnings("unchecked")
	public JPanel create(
			int w,
			int h,
			Map<String, BufferedImage> resourceSymbols,
			Map<String, Color> resourceColors,
			PropertyManager<Properties> propertyManager,
			Dimension dimension,
			ResourceCharts resourceCharts){

		gridProps = propertyManager;
		gridProps.addListener(Properties.REAL_TIME, Boolean.class, this);
		gridProps.addListener(Properties.PAUSE, Integer.class, this);
		gridProps.addListener(Properties.CHARTS_REFRESH_RATE, Integer.class, this);
		gridProps.addListener(Properties.GRID_REFRESH_RATE, Integer.class, this);
		gridProps.addListener(Properties.EVAP_ON, Boolean.class, this);
		
		this.realTime = gridProps.get(Properties.REAL_TIME, Boolean.class);
		this.drawingRefreshRate = propertyManager.get(Properties.GRID_REFRESH_RATE, Integer.class);
		this.chartsRefreshRate = propertyManager.get(Properties.CHARTS_REFRESH_RATE, Integer.class);
		this.evapOn = propertyManager.get(Properties.USE_ATTRACTOR, Boolean.class);
		int nbResources = propertyManager.get(Properties.RESOURCE_NUMBER, Integer.class);
		int memoryLength = propertyManager.get(Properties.MEMORY_LENGTH, Integer.class);
		this.pause = propertyManager.get(Properties.PAUSE, Integer.class);

		this.resourceCharts = resourceCharts;
		this.listeners = new EventListenerList();

		Cell<CellViewProperties>[][] cs = new Cell[w][h];

		ArrayList<short[]> la = new ArrayList<short[]>();

		for(short x=0; x<w; x++){
			for(short y=0; y<h; y++){
				short[] p = {x, y};
				cs[x][y] = new Cell<CellViewProperties>(new HashMap<String, Short>(), p);
				la.add(p);
			}
		}

		ArrayList<String> names = new ArrayList<String>(resourceSymbols.keySet());


		int n = nbResources/3;
		int lsize = la.size();
		for(String name : names){
			for(int i=0; i < n; i++){
				int j = (int)(Math.random()*lsize);
				short[] p = la.get(j);
				cs[p[0]][p[1]].addOneResource(name);
				la.remove(j);
				lsize--;
			}
		}

		gridModel = new GridModel<Cell<CellViewProperties>, AgentViewProperties, CellViewProperties>
				(cs,
				1f - propertyManager.get(Properties.EVAPORATION_RATE_PERCENT, Integer.class)/100f,
				propertyManager.get(Properties.VISION, Integer.class),
				propertyManager.get(Properties.PHERO_FORCE, Double.class),
				propertyManager.get(Properties.PHERO_RADIUS, Integer.class));

		// --- Fenêtre ---
		JPanel mainPanel = this;
		mainPanel.setLayout(new BorderLayout());

		grid = new Grid(gridModel, resourceSymbols, resourceColors, dimension, gridProps, memoryLength, resourceCharts);
		JScrollBar vertical = new JScrollBar(JScrollBar.VERTICAL, 0, 1, -gridModel.height, gridModel.height);
		vertical.addAdjustmentListener(new AdjustmentListener(){

			
			public void adjustmentValueChanged(AdjustmentEvent e) {
				grid.setYOffset(gridModel.height-e.getValue());
			}

		});
		Rectangle rv = vertical.getBounds();
		vertical.setBounds(new Rectangle(rv.x, rv.y, rv.width/2, rv.height));
		JScrollBar horizontal = new JScrollBar(JScrollBar.HORIZONTAL, 0, 1, -gridModel.width, gridModel.width);
		horizontal.addAdjustmentListener(new AdjustmentListener(){

			
			public void adjustmentValueChanged(AdjustmentEvent e) {
				grid.setXOffset(gridModel.width-e.getValue());
			}

		});
		mainPanel.add(horizontal, BorderLayout.SOUTH);
		mainPanel.add(vertical, BorderLayout.EAST);
		//frame.add(new JScrollPane(grid), BorderLayout.CENTER);
		mainPanel.add(grid, BorderLayout.CENTER);

		return mainPanel;
	}

	
	public void propertyChanged(PropertyChangedEvent<Properties> e) {
		switch(e.getKey()){
		case REAL_TIME :
			realTime = (Boolean)e.getValue();
			break;
		case PAUSE :
			pause = (Integer)e.getValue();
			break;
		case CHARTS_REFRESH_RATE :
			chartsRefreshRate = (Integer)e.getValue();
			break;
		case GRID_REFRESH_RATE :
			drawingRefreshRate = (Integer)e.getValue();
			break;
		case EVAP_ON :
			evapOn = (Boolean)e.getValue();
			break;
		default:
			throw new Error("Property not managed");
		}
		if(realTime){
			synchronized(this){
				this.notify();
			}
		}
	}

	public int getIterationNumber() {
		return iter;
	}

}
